package com.server.mongodb.mapper;

import com.proto_java.PBChatHistory;
import com.proto_java.PBGetChatHistory;
import com.proto_java.PBGroupInfo;
import com.server.mongodb.dao.UserManage;
import com.server.mongodb.domain.*;
import com.server.tools.Code;
import com.server.tools.ProtoBufTools;
import com.server.tools.PublicUtils;
import com.server.tools.http_exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Slf4j
@Component
public class UserManageImpl implements UserManage {

    @Autowired
    MongoTemplate mongoTemplate;

    public MGUser selectUser(Long uid, String password){
        // 用这种方式要注意字符串转义,比如这里password那边就必须转移""来标识它是一个字符串
        return mongoTemplate.findOne(
                Query.query(Criteria.where("uid").is(uid).and("password").is(password)
                        .and("exist").is(true)),
                // 传入类文件,这个方法就知到要去哪个文档查，查出来的格式是什么样子
                MGUser.class);
    }

    @Override
    public MGUser selectUser(Long uid) {
        return mongoTemplate.findOne(
                Query.query(Criteria.where("uid").is(uid).and("exist").is(true)),
                        MGUser.class);
    }

    @Override
    public MGGroup selectGroup(Long uid) {
        return mongoTemplate.findOne(
                Query.query(Criteria.where("uid").is(uid).and("exist").is(true)),
                        MGGroup.class);
    }

    @Override
    public MGUser selectUserList(Long uid) {
        return mongoTemplate.findOne(
                Query.query(Criteria.where("uid").is(uid)),
                MGUser.class);
    }

    @Override
    public MGGroup selectGroupList(Long uid) {
        return mongoTemplate.findOne(
                Query.query(Criteria.where("uid").is(uid)),
                MGGroup.class);
    }

    @Override
    public Map<Long,Integer> getListUnreadMsg(List<Long> dataList,Long userUid,boolean isGroup){

        //如果它没有好友或群的列表数据,则返回null
        if(dataList==null){
            return null;
        }
        Map<Long,Integer > map = new HashMap<>();
        // 根据是否是群消息做不同查询
        if(isGroup){
            // 如果是群列表
            for (Long p : dataList) {
                List<MGChatHistory> data = mongoTemplate.find(Query.query(
                                Criteria.where("groupUid").is(p)
                                .and("GroupMemberUnreadUid").in(userUid)), MGChatHistory.class);
                // 如果没有未读消息则赋值0,如果有就赋值对应数量
                map.put(p,data.isEmpty()?0:data.size());
            }
        }else{
            // 如果是好友列表
            for (Long p : dataList) {
                List<MGChatHistory> data = mongoTemplate.find(Query.query(
                                Criteria.where("sendUserUid").is(p).and("getUserUid").is(userUid)
                                        .and("isRead").is(false)), MGChatHistory.class);
                map.put(p,data.isEmpty()?0:data.size());
            }
        }
    return map;
    }

    @Override
    public List<MGChatHistory> getInitListHistory(List<Long> getGroupAndFriendHaveChatHistory,Long userUid){

        //如果它没有好友或群的数据,则返回null
        if(getGroupAndFriendHaveChatHistory==null){
            return null;
        }

        List<MGChatHistory> lastData =new ArrayList<>();

        for(Long p : getGroupAndFriendHaveChatHistory){
            // 将最新的一条数据添加进集合中
            MGChatHistory one = mongoTemplate.findOne(Query.query(
                    // 如果是群uid,则必然不会使第二句成立,从而拿到对应群消息
                    new Criteria().orOperator(Criteria.where("groupUid").is(p),
                            // 同理,如果是好友uid也不可能再第一句查出群消息,从而避免数据重复
                            new Criteria().orOperator(Criteria.where("sendUserUid").is(p)
                                            .and("getUserUid").is(userUid),
                                    Criteria.where("getUserUid").is(p)
                                            .and("sendUserUid").is(userUid)))
                    // 降序排序,只取最新的一条
            ).with(Sort.by(Sort.Direction.DESC,"sendDate")), MGChatHistory.class);
            // 没查到的一定是群,因为好友必然有一条打招呼信息
            // 如果群里没消息,则直接跳过,消息列表自然就不展示没消息的了
            if(one!=null){
                lastData.add(one);
            }
        }
        return lastData;
    }

    @Transactional
    @Override
    public Long register(String name, String password) {
        // 将uid计数器+1并返回+1后的uid结果作为新用户的uid
        UIDCounter uid = mongoTemplate.findAndModify(Query.query(Criteria.where("_id").is(new ObjectId("668d38bcd5893b8c42add3ff")))
                , new Update().inc("uidCounter", 1), FindAndModifyOptions.options().returnNew(true), UIDCounter.class);
        // 如果操作失败抛出异常
        if(uid==null || uid.getUidCounter() ==null ){
            throw new BusinessException("新用户uid获取失败", Code.SELECT_ERR);
        }
        // 成功后将数据整理并新增用户
        MGUser insert = mongoTemplate.insert(new MGUser(uid.getUidCounter(), name, password, "/default/headIcon.jpg", new ArrayList<>(), new ArrayList<>(),true));
        // 返回uid
        return insert.getUid();
    }

    @Override
    public Long deleteAccount(Long uid) {
        MGUser  user =selectUser(uid);
        return mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(uid)),
                new Update().set("exist",false)
                        .set("name",user.getName()+"([该用户已注销]"),MGUser.class).getModifiedCount();
    }

    @Override
    public Long updatePassword(Long uid, String password, String newPassword) {
        long modified=selectUser(uid,password)==null?-1:0; // 先用旧密码验证一下是否有该用户
        if(modified==0){
            modified=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(uid)),
                    new Update().set("password",newPassword),MGUser.class).getModifiedCount();
        }
        return modified;
    }

    @Override
    public Long updateHeadImageOrName(Long uid, String name, String headImage) {
        return mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(uid)),
                new Update().set("name",name).set("headImage",headImage),MGUser.class).getMatchedCount();
    }

    @Transactional
    @Override
    public MGGroup createGroup(Long uid, String name) {
        // 将uid计数器+1并返回+1后的uid结果作为新用户的uid
        UIDCounter groupUid = mongoTemplate.findAndModify(Query.query(Criteria.where("_id").is(new ObjectId("668d38bcd5893b8c42add3ff")))
                , new Update().inc("uidCounter", 1), FindAndModifyOptions.options().returnNew(true), UIDCounter.class);
        // 如果操作失败抛出异常
        if(groupUid==null || groupUid.getUidCounter() ==null ){
            throw new BusinessException("新用户uid获取失败", Code.SELECT_ERR);
        }
        // 成功后将数据整理并新增用户
        List<Long> list = new ArrayList<>();
        list.add(uid);
        // 添加进用户信息,返回uid
        MGGroup mgGroup= mongoTemplate.insert(new MGGroup(groupUid.getUidCounter(), name, "/default/headIcon.jpg", list,uid,true));
        long user=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(uid)),
                new Update().push("groupUid",groupUid.getUidCounter()),MGUser.class).getModifiedCount();
        return user==1?mgGroup:null;
    }

    @Override
    public MGAddRequest addRequest(Long getUid, Long sendUid, boolean isGroup) {
        MGGroup group=null;
            if(isGroup){
                group=selectGroup(getUid);
            }
        return mongoTemplate.insert(new MGAddRequest(getUid,sendUid,
                group==null?-1:group.getGroupBossUid(),isGroup,false));
    }

    @Override
    public boolean isRepeat(Long getUid, Long sendUid, boolean isGroup) {
        return mongoTemplate.exists(Query.query(Criteria.where("uid").is(sendUid).and(
                        isGroup?"groupUid":"friendUid").in(getUid)),
                MGUser.class);
    }

    @Transactional
    @Override
    public boolean executeAddRequest(Long getUid, Long sendUid) {
        MGAddRequest addRequest=mongoTemplate.findOne(
                Query.query(Criteria.where("getUid").is(getUid).and("sendUid").is(sendUid)),
                MGAddRequest.class);
        if(addRequest==null){ // 如果查不到,返回false
            return false;
        }

        // 根据内容添加数据并清空不需要的请求
        String pushObject1="friendUid";
        String pushObject2="friendUid";
        boolean mgGroupClass = false;
        if(addRequest.isGroup()){
            pushObject1="memberUid";
            pushObject2="groupUid";
            mgGroupClass = true;
        }
            // 接收方和发送方都要添加数据
            long result=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(addRequest.getGetUid())),
                    new Update().push(pushObject1,addRequest.getSendUid()),mgGroupClass?MGGroup.class:MGUser.class).getModifiedCount();
            long result2=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(addRequest.getSendUid())),
                    new Update().push(pushObject2,addRequest.getGetUid()),MGUser.class).getModifiedCount();

            if(result2==result && result==1){
                mongoTemplate.remove(Query.query(Criteria.where("getUid").is(getUid).and("sendUid").is(sendUid)),
                        MGAddRequest.class);
                return true;
            }
        return false;
    }

    @Transactional
    @Override
    public boolean disbandGroup(Long groupBossUid, Long groupUid) {
        MGGroup group =selectGroup(groupUid);
        long modified=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(groupUid)
                        .and("groupBossUid").is(groupBossUid)),
                new Update().set("exist",false)
                        .set("name",group.getName()+"[该群已解散]"), MGGroup.class).getModifiedCount();
        // 看一下是否群聊成功解散,然后处理群主的群列表，返回结果
        if(modified==1){
            return mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(groupBossUid)),
                    new Update().pull("groupUid",groupUid),MGUser.class).getModifiedCount()==1;
        }
        return false;
    }

    @Override
    public boolean disbandFriend(Long friendUid, Long userUid) {
        // 删除好友
        return mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(userUid)),
                new Update().pull("friendUid",friendUid),MGUser.class).getModifiedCount()==1;
    }

    @Override
    public boolean deleteAddRequest(Long sendUid, Long getUid) {
        // wasAcknowledged方法返回是否正常执行了删除操作
        return mongoTemplate.remove(Query.query(Criteria.where("getUid").is(getUid).and("sendUid").is(sendUid)),
                MGAddRequest.class).wasAcknowledged();
    }

    @Override
    public List<MGAddRequest> getAddRequestFriend(Long uid) {
        return mongoTemplate.find(
                Query.query(Criteria.where("getUid").is(uid)),
                MGAddRequest.class);
    }

    @Override
    public List<MGAddRequest> getAddRequestGroup(Long uid) {
        return mongoTemplate.find(
                Query.query(Criteria.where("groupBossUid").is(uid)),
                MGAddRequest.class);
    }

    @Override
    public void readAlreadyAddRequest(Long uid, boolean isGroup) {
       if(isGroup){
           mongoTemplate.updateMulti(Query.query(Criteria.where("groupBossUid").is(uid)),
                   new Update().set("isRead",true),MGAddRequest.class);
       }else{
           mongoTemplate.updateMulti(Query.query(Criteria.where("getUid").is(uid)),
                   new Update().set("isRead",true),MGAddRequest.class);
       }
    }

    @Override
    public boolean insertChatHistory(PBChatHistory.ChatHistory data, boolean isGroup) {

        boolean result;
        if(isGroup){
            result=mongoTemplate.exists(Query.query(Criteria.where("uid").is(data.getGroupUid())
                    .and("memberUid").in(data.getSendUserUid())),MGGroup.class);
        }else{
            result=mongoTemplate.exists(Query.query(Criteria.where("uid").is(data.getGetUserUid())
                    .and("friendUid").in(data.getSendUserUid())),MGUser.class);
        }
        if(!result){
            return false;
        }

        String lastData="";
        if(data.getIsImage()){
           lastData= PublicUtils.saveImage(data.getData(),"/chatImage/");
           // 这样处理data可以少复制一次图片数据的内存
           lastData="".equals(lastData)?"":lastData;
        }else{
            // 否则就直接将字符串给它
            lastData=data.getData();
        }


        if(isGroup){
            MGGroup group=selectGroup(data.getGroupUid());
            // 说明群聊被解散了
            if(group==null){
                return false;
            }
            List<Long> memberUid=group.getMemberUid();
            // 删掉成员中发消息的uid
            Iterator<Long> iterator =memberUid.iterator();
            while(iterator.hasNext()){
                Long p=iterator.next();
                if(p.equals(data.getSendUserUid())){
                    iterator.remove();
                }
            }
            mongoTemplate.insert(
                    new MGChatHistory(lastData,data.getIsImage(),data.getSendUserUid(),
                            selectUser(data.getSendUserUid()).getHeadImage(),data.getSendUserName(),
                            null,null,null,
                            new Date(Long.parseLong(data.getSendDate())),false,
                            data.getGroupUid(),data.getGroupName(),
                            group.getHeadImage(),memberUid));
        }else{
            MGUser user=selectUser(data.getGetUserUid());
            // 说明收消息的人注销了
            if(user==null){
                return false;
            }
            mongoTemplate.insert(
                    new MGChatHistory(lastData,data.getIsImage(),data.getSendUserUid(),
                            selectUser(data.getSendUserUid()).getHeadImage(),data.getSendUserName(),
                            selectUser(data.getGetUserUid()).getHeadImage(),data.getGetUserName(),
                            data.getGetUserUid(),new Date(Long.parseLong(data.getSendDate())),
                            false,null,null,null,null));
        }
        return true;

    }

    @Transactional
    @Override
    public List<Long> getGroupAndFriendHaveChatHistory(List<Long> friendUid, List<Long> groupUid,Long userUid) {

        List<Long> lastData=new ArrayList<>();
        for (Long p : friendUid) {
            MGChatHistory user = mongoTemplate.findOne(
                    Query.query(new Criteria().orOperator(Criteria.where("sendUserUid").is(p)
                                    .and("getUserUid").is(userUid),
                            Criteria.where("getUserUid").is(p)
                                    .and("sendUserUid").is(userUid))),
                    MGChatHistory.class);
            if(user!=null){
                lastData.add(p);
            }
        }
        for (Long p : groupUid) {
            MGChatHistory group = mongoTemplate.findOne(
                    Query.query(Criteria.where("groupUid").is(p)),MGChatHistory.class);
            if(group!=null){
                lastData.add(p);
            }
        }
        return lastData;
    }

    @Override
    public List<MGChatHistory> getChatHistory(PBGetChatHistory.GetChatHistory getChatHistory) {

        if(getChatHistory.getIsGroup()){
            return mongoTemplate.find(Query.query(Criteria.where("groupUid").is(getChatHistory.getUid())),
                    MGChatHistory.class);
        }else{
            return mongoTemplate.find(
                    Query.query(new Criteria().orOperator(Criteria.where("sendUserUid").is(getChatHistory.getUid())
                                    .and("getUserUid").is(getChatHistory.getUserUid()),
                            Criteria.where("getUserUid").is(getChatHistory.getUid())
                                    .and("sendUserUid").is(getChatHistory.getUserUid()))),
                    MGChatHistory.class);
        }
    }


    @Override
    public void readAlreadyChatHistory(Long sendOrGroupUid, Long userUid, boolean isGroup) {

        if(isGroup){
            mongoTemplate.updateMulti(Query.query(Criteria.where("groupUid").is(sendOrGroupUid)
                            .and("groupMemberUnreadUid").in(userUid)),
                    new Update().pull("groupMemberUnreadUid", userUid),MGChatHistory.class);
        }else{
            mongoTemplate.updateMulti(Query.query(Criteria.where("sendUserUid").is(sendOrGroupUid)
                            .and("getUserUid").is(userUid)),
                    new Update().set("isRead",true),MGChatHistory.class);
        }

    }

    @Transactional
    @Override
    public boolean exitGroup(Long userUid, Long groupUid) {
        boolean finalResult=false;
        finalResult=mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(userUid)),
                new Update().pull("groupUid",groupUid),MGUser.class).getModifiedCount()==1;
        if(finalResult){
            return mongoTemplate.updateFirst(Query.query(Criteria.where("uid").is(groupUid)),
                    new Update().pull("memberUid",userUid),MGGroup.class).getModifiedCount()==1;
        }
        return false;
    }


}
